<template>
  <div class="error-overlay" v-show="showOverlay" @keydown.ctrl.a.prevent="hideOverlay" tabindex="0">
    <div class="error-message">Error 500</div>
  </div>
  <div class="container">
    <el-card class="box-card">
      <template #header>
        <div class="card-header">
          <h2>查询工具</h2>
        </div>
      </template>

      <el-form :model="form" label-width="120px">
        <el-form-item label="DB ID">
          <el-input v-model="form.databaseId" placeholder="DB ID"></el-input>
        </el-form-item>

        <el-form-item label="验证码">
          <el-input v-model="form.totpCode" placeholder="请输入账号名称:动态码"></el-input>
        </el-form-item>

        <el-form-item label="每个文件记录数">
          <el-input-number v-model="form.maxRecordsPerFile" :min="1" :max="10000"></el-input-number>
        </el-form-item>

        <el-form-item label="查询语句">
          <div class="editor-wrapper">
            <div ref="editorRef" class="editor"></div>
            <el-button class="format-btn" type="primary" @click="formatSQL" :loading="formatting">格式化</el-button>
          </div>
        </el-form-item>

        <el-form-item>
          <el-button type="primary" @click="handleSubmit" :loading="loading">提交查询</el-button>
        </el-form-item>
      </el-form>
    </el-card>
  </div>
</template>

<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { ElMessage } from 'element-plus'
import CryptoJS from 'crypto-js'
import sha256 from 'crypto-js/sha256'
import axios from 'axios'
import { EditorView } from '@codemirror/view'
import { EditorState } from '@codemirror/state'
import { sql } from '@codemirror/lang-sql'
import { oneDark } from '@codemirror/theme-one-dark'
import {
  keymap, lineNumbers, highlightActiveLineGutter, highlightSpecialChars,
  drawSelection, dropCursor, rectangularSelection, crosshairCursor,
  highlightActiveLine
} from '@codemirror/view'
import {
  foldGutter, indentOnInput, syntaxHighlighting,
  defaultHighlightStyle, bracketMatching, foldKeymap
} from '@codemirror/language'
import { history, defaultKeymap, historyKeymap } from '@codemirror/commands'
import { highlightSelectionMatches } from '@codemirror/search'
import { format } from 'sql-formatter'

// 添加遮罩层控制变量，默认true隐藏 ctrl+a隐藏
const showOverlay = ref(true)

// 隐藏遮罩层的方法
const hideOverlay = () => {
  showOverlay.value = false
}

const form = ref({
  databaseId: '',
  totpCode: '',  //  
  maxRecordsPerFile: 1000,
  sql: ''
})

const loading = ref(false)
const formatting = ref(false)
const editorRef = ref(null)
let editorView = null

onMounted(() => {
  // 添加焦点到遮罩层，使其能接收键盘事件
  const overlay = document.querySelector('.error-overlay')
  if (overlay) {
    overlay.focus()
  }
  
  const startState = EditorState.create({
    doc: form.value.sql,
    extensions: [
      lineNumbers(),
      highlightActiveLineGutter(),
      highlightSpecialChars(),
      history(),
      foldGutter(),
      drawSelection(),
      dropCursor(),
      indentOnInput(),
      syntaxHighlighting(defaultHighlightStyle, { fallback: true }),
      bracketMatching(),
      rectangularSelection(),
      crosshairCursor(),
      highlightActiveLine(),
      highlightSelectionMatches(),
      keymap.of([
        ...defaultKeymap,
        ...historyKeymap,
        ...foldKeymap
      ]),
      sql(),
      oneDark,
      EditorView.updateListener.of(update => {
        if (update.docChanged) {
          form.value.sql = update.state.doc.toString()
        }
      })
    ]
  })

  editorView = new EditorView({
    state: startState,
    parent: editorRef.value
  })
})

onBeforeUnmount(() => {
  if (editorView) {
    editorView.destroy()
  }
})

const formatSQL = async () => {
  try {
    formatting.value = true
    const sql = form.value.sql.trim()
    if (!sql) {
      ElMessage.warning('请先输入查询语句')
      return
    }

    // 检查SQL语句类型
    const sqlLower = sql.toLowerCase().trim()
    const sqlTypeRegex = /^\s*(insert|update|delete)\b/i
    if (sqlTypeRegex.test(sqlLower)) {
      ElMessage.error('禁止使用INSERT、UPDATE、DELETE语句')
      return
    }

    // 检查基本SQL语法
    if (!sqlLower.includes('select') || !sqlLower.includes('from')) {
      ElMessage.error('语法错误：缺少必要的SELECT和FROM子句')
      return
    }

    try {
      // 尝试格式化，如果有语法错误会抛出异常
      const formattedSQL = format(sql, {
        language: 'mysql', // 指定数据库类型
        keywordCase: 'upper' // 关键字大写
      })

      if (editorView) {
        const transaction = editorView.state.update({
          changes: { from: 0, to: editorView.state.doc.length, insert: formattedSQL }
        })
        editorView.dispatch(transaction)
      }
      ElMessage.success('格式化成功')
    } catch (formatError) {
      // 捕获格式化过程中的语法错误
      ElMessage.error('语法错误：' + formatError.message)
      return
    }

    // 进行其他语法检查
    const errorMessages = []

    // 检查SELECT *
    if (sqlLower.includes('select *')) {
      errorMessages.push('建议：明确指定查询的列名，避免使用 SELECT *')
    }

    // 检查WHERE子句
    if (!sqlLower.includes('where') && !sqlLower.includes('limit')) {
      errorMessages.push('提示：没有WHERE条件和LIMIT限制可能会返回大量数据')
    }

    // 显示警告信息
    if (errorMessages.length > 0) {
      errorMessages.forEach(msg => {
        ElMessage.warning(msg)
      })
    }

  } catch (error) {
    ElMessage.error('格式化失败：' + error.message)
  } finally {
    formatting.value = false
  }
}


const handleSubmit = async () => {
  // 表单验证
  if (!form.value.databaseId || !form.value.sql) {
    ElMessage.error('请填写所有必填字段')
    return
  }

  // 验证TOTP格式
  if (form.value.totpCode != "" && !form.value.totpCode.includes(':')) {
    ElMessage.error('验证码格式错误，应为"账号名称:动态码"')
    return
  }

  const sqlLower = form.value.sql.toLowerCase().trim()

  // 检查SQL语句类型
  if (sqlLower.includes('insert') || sqlLower.includes('update') || sqlLower.includes('delete')) {
    ElMessage.error('禁止使用INSERT、UPDATE、DELETE语句')
    return
  }

  if (!sqlLower.startsWith('select')) {
    ElMessage.error('只支持SELECT查询语句')
    return
  }

  try {
    loading.value = true

    // 从TOTP验证码中提取账号和动态码
   // // const [account, code] = form.value.totpCode.split(':')
    // 使用生成的密钥进行AES加密
    // const now = new Date();
    // const year = now.getFullYear();
    // const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从0开始，所以加1
    // const day = String(now.getDate()).padStart(2, '0');
    // const hour = String(now.getHours()).padStart(2, '0');
    // let nowdateStr = `${year}${month}${day}${hour}`;
    // let aesKey = CryptoJS.SHA256(form.value.totpCode + nowdateStr).toString().toString(CryptoJS.enc.Hex);
    // aesKey = aesKey.substring(aesKey.length - 24, aesKey.length);
    // const key = CryptoJS.enc.Utf8.parse(aesKey)
    // let encryptedDatabaseId = CryptoJS.AES.encrypt(form.value.databaseId, key, {
    //   mode: CryptoJS.mode.ECB,
    //   padding: CryptoJS.pad.Pkcs7
    // }).ciphertext.toString(CryptoJS.enc.Hex)
    // let encryptedSql = CryptoJS.AES.encrypt(form.value.sql, key, {
    //   mode: CryptoJS.mode.ECB,
    //   padding: CryptoJS.pad.Pkcs7
    // }).ciphertext.toString(CryptoJS.enc.Hex)



    let  jsonx={
        d: form.value.databaseId,
        s: form.value.sql,
        t: form.value.totpCode, // 发送TOTP验证信息
        m: form.value.maxRecordsPerFile
      }
      
    // 将JSON转换为十六进制
    let jsonString = JSON.stringify(jsonx)
    let jsonHex = CryptoJS.enc.Utf8.parse(jsonString).toString(CryptoJS.enc.Hex)
      

    //将encryptedSql的base64中等于符号替换成*号
    // encryptedDatabaseId = encryptedDatabaseId.replace(/=/g, '@') 
    // encryptedSql = encryptedSql.replace(/=/g, '@') 
    // 发送请求，不直接指定responseType为blob
    const response = await axios({
      url: '/api/qdata',
      method: 'POST',
      responseType: 'arraybuffer', // 使用arraybuffer而不是blob
      headers: {
        'Content-Type': 'text/plain'
      },
      data: jsonHex // 直接发送十六进制文本
    })

    // 检查响应类型
    const contentType = response.headers['content-type']
    // console.log('响应内容类型:', contentType)
    
    // 如果是文本类型，说明是错误信息
    if (contentType && contentType.includes('text/plain')) {
      const textDecoder = new TextDecoder('utf-8')
      const errorText = textDecoder.decode(response.data)
      ElMessage.error('失败：' + errorText)
      return
    }
    
    // 下载文件
    const blob = new Blob([response.data], { type: contentType || 'application/zip' })
    const url = window.URL.createObjectURL(blob)
    const link = document.createElement('a')
    link.href = url
    link.download = `export_${new Date().getTime()}.zip`
    document.body.appendChild(link)
    link.click()
    document.body.removeChild(link)
    window.URL.revokeObjectURL(url)

    ElMessage.success('成功')
  } catch (error) {
    console.error('请求错误:', error)
    // 如果有响应数据，尝试解析错误信息
    if (error.response) {
      const contentType = error.response.headers['content-type']
      console.log('错误响应内容类型:', contentType)
      
      if (contentType && contentType.includes('text/plain')) {
        // 如果是文本类型的错误
        if (error.response.data instanceof ArrayBuffer) {
          const textDecoder = new TextDecoder('utf-8')
          const errorText = textDecoder.decode(error.response.data)
          ElMessage.error('失败：' + errorText)
        } else {
          ElMessage.error('失败：' + error.response.data)
        }
      } else {
        ElMessage.error('请求失败：' + error.response.status)
      }
    } else {
      ElMessage.error('失败：' + error.message)
    }
  } finally {
    loading.value = false
  }
}




</script>

<style scoped>
/* 添加遮罩层样式 */
.error-overlay {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: white;
  z-index: 9999;
  display: flex;
  justify-content: center;
  align-items: center;
  outline: none; /* 移除焦点时的轮廓 */
}

.error-message {
  font-size: 32px;
  color: #333;
  font-weight: bold;
}

.container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

.box-card {
  margin-bottom: 20px;
}

.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.editor-wrapper {
  position: relative;
  width: 100%;
  height: 300px;
  border: 1px solid #dcdfe6;
  border-radius: 4px;
}

.editor {
  width: 100%;
  height: 100%;
}

.format-btn {
  position: absolute;
  right: 10px;
  top: 10px;
  z-index: 1;
}

:deep(.cm-editor) {
  height: 100%;
  width: 100% !important;
}

:deep(.cm-editor .cm-scroller) {
  font-family: monospace;
  line-height: 1.5;
}
</style>